
/// SOLUZIONE CON J VARIABILI TARGA STATICHE

#include "channel.h"
#include "shm_common.h"
#include <sys/shm.h>
#include <pthread.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>


struct channel_t
{
	union {
		struct {
			int type; // inheredited from base class (see shm.c)

			key_t key;
			int id;

			key_t altKey; // when set, receiver is receiving on this channel thru an Alternative

			pthread_mutex_t mutex;
			pthread_cond_t cond;

			int size; // message size
			int fixedsize; // message size as a multiple of 8 bytes (due to cache line problems...)
			int messages; // sent messages counter (0 or 1)
			int k; // asynchrony grade
			int j; // how many static vtgs are allocated in memory
			int nextVtg; // which is the next vtg to use
			int currVtg; // last vtg used
		};
		char null[1024];
	};
};

// returns the address of the i-th message of channel ch
#define MSG(ch,i)    ( (char*) ch + sizeof(*ch) + ch->fixedsize*i )

#define VTGCOUNT(ch)       ( ch->k + ch->j )


// creates a new channel
// the channel will contain k+1+j vtgs
// j has to be at least 1, for the channel to be safe
struct channel_t *LCcreate_as( key_t key, size_t size, int k, int j )
{
	struct channel_t *ch;
	int fixedsize = size + ( LINE_SIZE - size%LINE_SIZE );

	int id = shmget( key, sizeof(*ch) + fixedsize*(k+1+j), IPC_CREAT | 0700 );
	if( id == -1 ) {
		printf( "receiver::shmget(%x[%d], %d, %x): %s\n", key, key, (int) ( sizeof(*ch) + fixedsize*(k+1+j) ), IPC_CREAT | 0700, strerror(errno) );
		return NULL;
	}

	// ch = shmat( id, 0, SHM_RDONLY );
	ch = shmat( id, 0, 0 );
	if( ch == (void*) -1 ) { // TODO: not NULL? O,o
		printf( "receiver::shmat(%d, %d, %d): %s\n", id, 0, 0, strerror(errno) );
		return NULL;
	}

	// setting channel type
	ch->type = ASYNCHRONOUS_SYMMETRIC;

	// setting channel key and id
	ch->key = key;
	ch->id = id;

	// initing mutex and condition variable
	// (it's mandatory to set them as shared objects!)
	mutex_init( & ch->mutex );
	cond_init( & ch->cond );

	// setting other fields of the channel
	ch->size = size;
	ch->fixedsize = fixedsize;
	ch->messages = 0;

	ch->k = k;
	ch->j = 1+j;
	ch->nextVtg = 0;
	ch->currVtg = -1;

	ch->altKey = 0;

	return ch;
}

inline size_t LCgetChannelTypeSize_as( struct channel_t *ch )
{
	return ch->size;
}

inline bool LCreadyToReceive_as( struct channel_t *ch )
{
	return ch->messages > 0;
}
inline bool LCreadyToSend_as( struct channel_t *ch )
{
	return ch->messages <= ch->k;
}

// maps a shared channel on the virtual memory
struct channel_t *LCattach_as( key_t key, size_t size, int k, int j )
{
	struct channel_t *ch;

	int id = shmget( key, sizeof(*ch) + size*(k+1+j), 0700 );
	if( id == -1 ) {
		printf( "[AS] sender::shmget(): %s\n", strerror(errno) );
		return NULL;
	}

	ch = shmat( id, 0, 0 );
	if( ch == (void*) -1 ) {
		printf( "[AS] sender::shmat(): %s\n", strerror(errno) );
		return NULL;
	}

	return ch;
}

struct channel_t *LCreattach_as( struct channel_t *ch )
{
	key_t key = ch->key;
	size_t size = ch->fixedsize;
	int k = ch->k;
	int j = ch->j-1;

	return LCattach_as( key, size, k, j );
}

// destroys a channel
void LCdestroy_as( struct channel_t *ch )
{
	pthread_mutex_destroy( & ch->mutex );
	pthread_cond_destroy( & ch->cond );

	shmctl( ch->id, IPC_RMID, 0 );
}

// removes a channel from the virtual memory
void LCdetatch_as( struct channel_t *ch )
{
	shmdt( ch );
}

// sends a message on a channel
void LCsend_as( struct channel_t *ch, char *msg )
{
	__LClockChannel( ch );

	//printf( "COPIO: %p->%p %d\n", msg, MSG(ch,ch->nextVtg), ch->fixedsize - LINE_SIZE );
	//printf( "COPIO: %p->%p %d\n", msg, MSG(ch,ch->nextVtg), ch->size );
	/*
	//memcpy( MSG(ch,ch->nextVtg), msg, ch->size );
	int i;
	for( i = 0; i < ch->size/sizeof(long double); i ++ ) {
		* (long double*) ( MSG(ch,ch->nextVtg)+i ) = * (long double*) ( msg+i );
	}
	*/

	memcpy( MSG(ch,ch->nextVtg), msg, ch->fixedsize - LINE_SIZE );
	int i;
	for( i = ch->fixedsize - LINE_SIZE; i < ch->size; i ++ ) {
		*( MSG(ch,ch->nextVtg)+i ) = *( msg+i );
	}


	ch->messages ++;
	ch->nextVtg = ( ch->nextVtg + 1 ) % ( ch->k + ch->j );
	__LCwakeUpChannel( ch );
	if( ch->altKey ) {
		__LCwakeUpAlternative( ch->altKey );
	}

	while( ! LCreadyToSend_as(ch) ) {
		__LCwaitOnChannel( ch );
	}

	__LCunlockChannel( ch );
}

// receives a message from a channel
char *LCreceive_as( struct channel_t *ch )
{
	char *vtg;

	__LClockChannel( ch );
	while( ! LCreadyToReceive_as(ch) ) {
		__LCwaitOnChannel( ch );
	}

	ch->currVtg = ( ch->currVtg + 1 ) % (ch->k + ch->j);
	vtg = MSG(ch, ch->currVtg);
	ch->messages --;

	__LCwakeUpChannel( ch );
	__LCunlockChannel( ch );

	return vtg;
}



// prints debugging info about a channel
void printCH_as( struct channel_t *ch )
{
	int i;

	printf( "struct channel_t: %p\n"
			"    key: %d\n"
			"     id: %d\n"
			"   size: %d\n"
			"fxdsize: %d\n"
			"      k: %d\n"
			"      j: %d\n"
			"   next: %d\n"
			"   curr: %d\n"
			"   msgs: %d\n"
			"    alt: %d\n"
			"[%d bytes + %d per message: %d]\n",
			ch,
			ch->key,
			ch->id,
			ch->size,
			ch->fixedsize,
			ch->k,
			ch->j,
			ch->nextVtg,
			ch->currVtg,
			ch->messages,
			ch->altKey,
			(int) sizeof(*ch), ch->size, (int) sizeof(*ch)+ch->size );

	for( i = 0; i < (ch->k + ch->j); i ++ ) {
		printf( " msg[%d]: %s\n", i, MSG(ch,i) );
	}
}


